Latest update: December 2013
In this tutorial, we will show you how to upload a file to your FlashAir with
upload.cgi.
This tutorial builds off of
Android Tutorial 4: Displaying Image
Thumbnails.
We're going to sort images on your FlashAir by title, and by date.
Warning: If this operation is used incorrectly, the FlashAir file system may become corrupted and you may lose your data. Host devices, like a PC, can cache the contents of the SD card (FAT), but will not see changes made by upload.cgi. Therefore, a FAT file system conflict may occur if changes are made by the host device and upload.cgi at the same time. After using upload.cgi, re-insert the SD card and the host device will be able to see your changes.
We will add a Date List button to the content list.
A list, sorted by date, will be displayed when you tap the button.
Information will be displayed when you tap an item.
We will need to create the following files in order to write this application:
Important: Please note that your project contains a
file called
AndroidManifest.xml. This file is used to manage application permissions. By
default, applications
are not permitted to access the internet. The path to this file should look something like:
[Project_Folder]/AndroidManifest.xml
You will need to add the following line of code into your
AndroidManifest.xml in order for this application to work:
<uses-permission android:name="android.permission.INTERNET" />
Setup strings.xml as follows!
The path to this file should look something like:
[Project_Folder]/res/values/strings.xml
<string name="app_name">android_tutorial_07</string>
<string name="action_settings">Settings</string>
<string name="title_activity_image_view">ImageViewActivity</string>
<string name="date_list">Date List</string>
<string name="back">Back</string>
<string name="directory_name">Directory Name</string>
<string name="date">Date</string>
<string name="title">Title</string>
<string name="title_">Title:</string>
<string name="memo">Memo</string>
<string name="memo_">Memo:</string>
<string name="save">Save</string>
<string name="grid_view_image">grid_view_image</string>
Lets start with
activity_main.xml, which determines the layout of our Android App.
This can be found in your layout folder.
The path to this file should look something like:
[Project_Folder]/res/layout/activity_main.xml
This file will be identical to the activity_main.xml file from Android Tutorial 3: Downloading Content, except with a button added to it.
Add the following lines to to activity_main.xml.
<Button
android:id="@+id/button2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:cacheColorHint="#00000000"
android:text="@string/date_list"
android:textColor="@android:color/white"
android:textSize="20sp" />
Next, edit
activity_image_view.xml. This file determines the layout of our image viewing
screen.
This can also be found in your layout folder.
The path to this file should look something like:
[Project_Folder]/res/layout/activity_image_view.xml
This file will be identical to the
activity_main.xml file from
Android Tutorial 3: Downloading Content.
Please refer to that tutorial for an explanation of the implementation.
Next, write the
activity_date_list.xml and
list_view_item.xml files. They will determine the layout of our date list and the
layout
for the rows in the list.
They can be found in your layout folder.
The path to these files should look something like:
[Project_Folder]/res/layout/activity_date_list.xml
[Project_Folder]/res/layout/list_view_item.xml
You'll want the activity_date_list.xml file to look like this:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
tools:context=".DateListActivity" >
<Button
android:id="@+id/button1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:cacheColorHint="#00000000"
android:text="@string/back"
android:textColor="@android:color/white"
android:textSize="20sp" />
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:scaleType="centerInside"
android:text="@string/directory_name"
android:textAlignment="center"
android:textAppearance="?android:attr/textAppearanceLarge" />
<ListView
android:id="@+id/listView1"
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:cacheColorHint="#00000000"
android:clickable="true"
android:headerDividersEnabled="false" />
</LinearLayout>
And you'll want the list_view_item.xml file to look like this:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/date" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/title" />
</LinearLayout>
Next, we will write the
activity_memo_edit.xml and
grid_view_item.xml files.
activity_memo_edit.xml determines the layout of our operation screen.
grid_view_item.xml is for displaying the thumbnails.
They can be found in your layout folder.
The path to this file should look something like:
[Project_Folder]/res/layout/activity_memo_edit.xml
[Project_Folder]/res/layout/grid_view_item.xml
activity_memo_edit.xml should look something like this:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
tools:context=".MemoEditActivity" >
<Button
android:id="@+id/button1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:cacheColorHint="#00000000"
android:text="@string/back"
android:textColor="@android:color/white"
android:textSize="20sp" />
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:scaleType="centerInside"
android:text="@string/directory_name"
android:textAlignment="center"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView
android:id="@+id/textView2"
android:layout_width="152dp"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:gravity="center"
android:hint="@string/date"
android:paddingLeft="10dp"
android:textSize="18sp" />
<GridView
android:id="@+id/gridView1"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="0.01"
android:horizontalSpacing="10dp"
android:numColumns="5"
android:verticalSpacing="10dp" >
</GridView>
<TextView
android:id="@+id/TextView01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:layout_marginTop="12dp"
android:paddingLeft="10dp"
android:text="@string/title_"
android:textSize="18sp" />
<EditText
android:id="@+id/editTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="@string/title"
android:inputType="text"
android:textStyle="italic" >
</EditText>
<TextView
android:id="@+id/TextView02"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:layout_marginTop="20dp"
android:paddingLeft="10dp"
android:text="@string/memo_"
android:textSize="18sp" />
<EditText
android:id="@+id/editMemo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="@string/memo"
android:inputType="text"
android:textStyle="italic" />
<Button
android:id="@+id/button2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:cacheColorHint="#00000000"
android:text="@string/save"
android:textColor="@android:color/white"
android:textSize="20sp" />
</LinearLayout>
And grid_view_item.xml should look like this:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/grid_view_image"
/>
</LinearLayout>
Next, we will modify
MainActivity.java.
We will add the click behavior of the Date List button to
Android Tutorial 4: Displaying Image
Thumbnails.
And then we'll add a sort condition so we get only image files.
Please take MainActivity.java from Android Tutorial 4: Displaying Image Thumbnails and copy it into your new MainActivity.java file.
We will add member variables and replace the
onCreate(Bundle savedInstanceState)
function with the code below:
public class MainActivity extends Activity implements AdapterView.OnItemClickListener {
ListView listView;
ImageView imageView;
TextView currentDirText;
TextView numFilesText;
Button backButton;
Button datelistButton;
String rootDir = "DCIM";
String directoryName = rootDir; // Initialize to rootDirectory
SimpleAdapter listAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
// Set buttons
backButton = (Button)findViewById(R.id.button1);
getWindow().setTitleColor(Color.rgb(65, 183, 216));
backButton.getBackground().setColorFilter(Color.rgb(65, 183, 216),
PorterDuff.Mode.SRC_IN);
backButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(directoryName.equals(rootDir)) {
listRootDirectory();
}
else {
int index = directoryName.lastIndexOf("/");
directoryName = directoryName.substring(0, index);
listDirectory(directoryName);
}
}
});
backButton.setEnabled(false); // Disable in root directory
datelistButton = (Button)findViewById(R.id.button2);
getWindow().setTitleColor(Color.rgb(65, 183, 216));
datelistButton.getBackground().setColorFilter(Color.rgb(65, 183, 216),
PorterDuff.Mode.SRC_IN);
datelistButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent dateList = new Intent(getBaseContext(), DateListActivity.class);
dateList.putExtra("dir", currentDirText.getText());
MainActivity.this.startActivity(dateList);
}
});
listRootDirectory();
}
catch(Exception e) {
Log.e("ERROR", "ERROR: " + e.toString());
e.printStackTrace();
}
}
We added a Date List button to our previous
MainActivity
class.
Add an if statement to
listDirectory()
so that we only see image files.
if( (allFiles[i].toString().toLowerCase(Locale.getDefault()).endsWith(".jpg"))
|| (allFiles[i].toString().toLowerCase(Locale.getDefault()).endsWith(".jpeg"))
|| (allFiles[i].toString().toLowerCase(Locale.getDefault()).endsWith(".jpe"))
|| (allFiles[i].toString().toLowerCase(Locale.getDefault()).endsWith(".png")) ) {
// Image file
fileNames.add(allFiles[i]);
}
We want the list to be updated when the screen is restarted (for example when the user hits the back button).
@Override
public void onRestart(){
super.onRestart();
listDirectory(directoryName);
}
class ImageViewActivity
will be identical to the version in
Android Tutorial 3: Downloading Content.
Please refer to that tutorial for an explanation
of the implementation.
We will start by changing the class declaration, since we want to include a list that contains clickable items.
#### *DateListActivity.java* (1)
lined: public class DateListActivity extends Activity implements AdapterView.OnItemClickListener {
Then we'll declare the views, other class variables, and organize the screens format.
We will also override the
onCreate(Bundle savedInstanceState)
function that initializes the Activity
class.
We want the initialization function to set up the list as well as to create a click
listener
for the
Button
that we will use to return to the parent directory.
ListView listView;
ImageView imageView;
TextView currentDirText;
Button backButton;
String rootDir = "DCIM";
String directoryName;
SimpleAdapter listAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_date_list);
try {
Bundle extrasData = getIntent().getExtras();
directoryName = extrasData.getString("dir");
if(!directoryName.equals(rootDir)) {
int index = directoryName.lastIndexOf("/");
directoryName = directoryName.substring(0, index);
}
// Set buttons
backButton = (Button)findViewById(R.id.button1);
getWindow().setTitleColor(Color.rgb(65, 183, 216));
backButton.getBackground().setColorFilter(Color.rgb(65, 183, 216),
PorterDuff.Mode.SRC_IN);
backButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
DateListActivity.this.finish(); // Go back to Get screen
}
});
listDirectory(directoryName);
}
catch(Exception e) {
Log.e("ERROR", "ERROR: " + e.toString());
e.printStackTrace();
}
}
Button
that will allow the user to return to the parent directory. We just need to get the content list with
command.cgi
, and then get the file's timestamps as well.
This is to avoid problems when two files have the same name.
public void listRootDirectory() {
directoryName = rootDir;
listDirectory(directoryName);
}
public void listDirectory(String dir) {
// Prepare command directory path
currentDirText = (TextView)findViewById(R.id.textView1);
currentDirText.setText(dir + "/");
// Fetch list of items in directory and display in a ListView
new AsyncTask<String, Void, ListAdapter>(){
@Override
protected ListAdapter doInBackground(String... params) {
String dir = params[0];
String cmddir = "/" + dir;
ArrayList <NameValuePair> httpParams = new ArrayList <NameValuePair> ();
httpParams.add(new BasicNameValuePair("DIR", cmddir));
cmddir = URLEncodedUtils.format (httpParams, "UTF-8" );
Set <Integer> dates = new HashSet <Integer>();
String files = FlashAirRequest
.getString("http://flashair/command.cgi?op=100&" + cmddir);
String[] allFiles = files.split("([,\n])"); // split by newline or comma
for(int i = 2; i < allFiles.length; i= i + 6) {
if(allFiles[i].contains(".")) {
if( (allFiles[i].toString().toLowerCase(Locale.getDefault()).endsWith(".jpg"))
|| (allFiles[i].toString().toLowerCase(Locale.getDefault()).endsWith(".jpeg"))
|| (allFiles[i].toString().toLowerCase(Locale.getDefault()).endsWith(".jpe"))
|| (allFiles[i].toString().toLowerCase(Locale.getDefault()).endsWith(".png"))){
// Image file
Integer date = Integer.parseInt(allFiles[i+3]);
dates.add(date);
}
}
}
// Get Title
ArrayList<Map<String, Object>> data = new ArrayList<Map<String, Object>>();
for (Integer date : dates) {
Map<String, Object> entry = new HashMap<String, Object>();
String dataStr = FlashAirRequest.getString("http://flashair/"
+ dir + "/" + getDate(date, "") + ".txt");
String[] dataStrAry = dataStr.split("([\n])"); // split by newline or comma
entry.put("date", getDate(date, "/"));
if (dataStrAry.length >= 2) {
entry.put("title", dataStrAry[0]);
}
data.add(entry);
}
// Set the file list to a widget
listAdapter = new SimpleAdapter(DateListActivity.this,
data,
R.layout.list_view_item,
new String[]{"date", "title"},
new int[]{R.id.textView1, R.id.textView2});
return listAdapter;
}
@Override
protected void onPostExecute(ListAdapter listAdapter) {
listView = (ListView)findViewById(R.id.listView1);
ColorDrawable divcolor = new ColorDrawable(Color.rgb(17, 19, 58));
listView.setDivider(divcolor);
listView.setDividerHeight(1);
listView.setAdapter(listAdapter);
listView.setOnItemClickListener(DateListActivity.this);
}
}.execute(dir);
getDate()
generates the date string(YYYYMMDD) from the date format(16-bit
integer
in decimal notation) which is returned by using
op=100
with
command.cgi
.
ListView
in line 64.
ListView
and pass it the
listAdapter
we created earlier.
We will set the list behavior to be the same as it was in
Android Tutorial 4: Displaying Image
Thumbnails .
If the item clicked is the name of a date, the next screen will display the
operation screen
of that date.
@Override
public void onItemClick(AdapterView<?> l, View v, int position, long id) {
Object item = l.getItemAtPosition(position);
if(item instanceof Map<?, ?>) {
Map<String, Object> mapItem = (Map<String, Object>) item;
Object selectDate = mapItem.get("date");
// Next button to start new intent to allow day detail
Intent memoEdit = new Intent(getBaseContext(), MemoEditActivity.class);
memoEdit.putExtra("date", selectDate.toString());
memoEdit.putExtra("dir", directoryName);
DateListActivity.this.startActivity(memoEdit);
}
}
ListView
by using a
Map
object. We want the list to be updated when the screen is restarted (for example when the user hits the back button).
@Override
public void onRestart(){
super.onRestart();
listDirectory(directoryName);
}
The method which generates the date string(YYYYMMDD) from the 16-bit date format previously mentioned.
public String getDate(Integer date, String sep) {
return String.format("%04d", ((date >> 9) & 0x1FF)+1980)+sep+
String.format("%02d", (date >> 5) & 0xF)+sep+
String.format("%02d", date & 0x1F);
}
We will start by declaring the views and changing the class declaration. We will also
override the
onCreate(Bundle savedInstanceState)
function that initializes the Activity
class. We want
the list initialization and set a click listener for the
Button
.
public class MemoEditActivity extends Activity {
TextView dateText;
TextView currentDirText;
Button backButton;
Button saveButton;
EditText titleField;
EditText memoField;
GridView gridView;
String newTitle = "";
String newMemo = "";
String date;
String rootDir = "DCIM";
String directoryName;
ArrayList<String> fileNamelist;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_memo_edit);
Bundle extrasData = getIntent().getExtras();
date = extrasData.getString("date");
directoryName = extrasData.getString("dir");
getWindow().setTitleColor(Color.rgb(65, 183, 216));
// Set backButton
backButton = (Button) findViewById(R.id.button1);
backButton.getBackground().setColorFilter(Color.rgb(65, 183, 216), PorterDuff.Mode.SRC_IN);
backButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MemoEditActivity.this.finish(); // Go back
}
});
// Set saveButton
saveButton = (Button) findViewById(R.id.button2);
saveButton.getBackground().setColorFilter(Color.rgb(65, 183, 216), PorterDuff.Mode.SRC_IN);
saveButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getInput();
saveNewTitleMemo();
}
});
// Set titleField and memoField
titleField = (EditText) findViewById(R.id.editTitle);
titleField.setHintTextColor(Color.rgb(65, 183, 216));
memoField = (EditText) findViewById(R.id.editMemo);
memoField.setHintTextColor(Color.rgb(65, 183, 216));
// Set dateText
dateText = (TextView) findViewById(R.id.textView2);
dateText.setText(date);
// Get title and memo
new AsyncTask<String, Void, ArrayList<String>>() {
@Override
protected ArrayList<String> doInBackground(String... params) {
String dir = params[0];
ArrayList<String> rtnAry = new ArrayList<String>();
String files = FlashAirRequest.getString("http://flashair/"
+ dir + "/" + date.replaceAll("/", "") + ".txt");
String[] allFiles = files.split("([\n])"); // split by newline or comma
if (allFiles.length >= 2) {
// File
rtnAry.add(allFiles[0]);
rtnAry.add(allFiles[1]);
}
return rtnAry;
}
@Override
protected void onPostExecute(ArrayList<String> strary) {
// Set the data to a TextView
titleField = (EditText) findViewById(R.id.editTitle);
memoField = (EditText) findViewById(R.id.editMemo);
if (strary.size() > 0) {
titleField.setText(strary.get(0));
memoField.setText(strary.get(1));
} else {
titleField.setText("");
memoField.setText("");
}
}
}.execute(directoryName);
// Set gridView
gridView = (GridView) findViewById(R.id.gridView1);
gridView.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v,
int position, long id) {
Object item = parent.getItemAtPosition(position);
if (item instanceof Map<?, ?>) {
Map<String, Object> mapItem = (Map<String, Object>) item;
Object downloadFile = mapItem.get("fname");
if ((downloadFile.toString().toLowerCase(Locale.getDefault()).endsWith(".jpg"))
|| (downloadFile.toString().toLowerCase(Locale.getDefault()).endsWith(".jpeg"))
|| (downloadFile.toString().toLowerCase(Locale.getDefault()).endsWith(".jpe"))
|| (downloadFile.toString().toLowerCase(Locale.getDefault()).endsWith(".png")))
{
// Image file, download using ImageViewActivity
Intent viewImageIntent = new Intent(MemoEditActivity.this,
ImageViewActivity.class);
viewImageIntent.putExtra("downloadFile",downloadFile.toString());
viewImageIntent.putExtra("directoryName", directoryName);
MemoEditActivity.this.startActivity(viewImageIntent);
} // Not an image file, do nothing
}
}
});
listDirectory(directoryName);
}
This is the same method that we used earlier.
Except that we display thumbnails with
GridView
instead of
ListView
.
public void listDirectory(String dir) {
// Prepare command directory path
currentDirText = (TextView)findViewById(R.id.textView1);
currentDirText.setText(dir + "/");
final ProgressDialog waitDialog;
// Setting ProgressDialog
waitDialog = new ProgressDialog(this);
waitDialog.setMessage("Now downloading...");
waitDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
waitDialog.show();
ArrayList<NameValuePair> httpParams = new ArrayList<NameValuePair>();
httpParams.add(new BasicNameValuePair("DIR", dir));
dir = URLEncodedUtils.format(httpParams, "UTF-8");
// Fetch list of items in directory and display in a ListView
new AsyncTask<String, Void, ListAdapter>() {
@Override
protected ListAdapter doInBackground(String... params) {
String dir = params[0];
ArrayList<String> fileNames = new ArrayList<String>();
fileNamelist = new ArrayList<String>();
String files = FlashAirRequest
.getString("http://flashair/command.cgi?op=100&" + dir);
String[] allFiles = files.split("([,\n])"); // split by newline�@or comma
for (int i = 2; i < allFiles.length; i = i + 6) {
if (allFiles[i].contains(".") && allFiles[i+3].contains(getDate16(date))) {
if( (allFiles[i].toString().toLowerCase(Locale.getDefault()).endsWith(".jpg"))
|| (allFiles[i].toString().toLowerCase(Locale.getDefault()).endsWith(".jpeg"))
|| (allFiles[i].toString().toLowerCase(Locale.getDefault()).endsWith(".jpe"))
|| (allFiles[i].toString().toLowerCase(Locale.getDefault()).endsWith(".png"))){
// Image file
fileNames.add(allFiles[i]);
}
}
}
// Get thumbnails
ArrayList<Map<String, Object>> data = new ArrayList<Map<String, Object>>();
for (int i = 0; i < fileNames.size(); i++) {
String url = "";
url = "http://flashair/thumbnail.cgi?" + directoryName + "/" + fileNames.get(i);
Map<String, Object> entry = new HashMap<String, Object>();
Bitmap thumbnail = null;
BitmapDrawable drawnIcon = null;
if ((url.toLowerCase(Locale.getDefault()).endsWith(".jpg"))
|| (url.toLowerCase(Locale.getDefault()).endsWith(".jpeg"))) {
thumbnail = FlashAirRequest.getBitmap(url);
drawnIcon = new BitmapDrawable(getResources(), thumbnail);
}
if(thumbnail == null) {
entry.put("thmb", R.drawable.ic_launcher);
}
else {
entry.put("thmb", drawnIcon);
}
entry.put("fname", fileNames.get(i)); // Put file name onto the map
data.add(entry);
}
// Set the file list to a widget
SimpleAdapter listAdapter = new SimpleAdapter(
MemoEditActivity.this, data, R.layout.grid_view_item,
new String[] { "thmb", "fname" },
new int[] {R.id.imageView1, android.R.id.text1 });
listAdapter.setViewBinder(new CustomViewBinder());
return listAdapter;
}
@Override
protected void onPostExecute(ListAdapter adapter) {
waitDialog.dismiss();
gridView.setAdapter(adapter);
}
}.execute(dir);
}
getDate16()
generates the date string in a date format(16-bit integer in
decimal notation)
from the target date format(YYYY/MM/DD).
GridView
in line 68.
Here we'll create a custom
ViewBinder
class, which will be used to get thumbnails.
GridView
.
class CustomViewBinder implements ViewBinder {
@Override
public boolean setViewValue(View view, Object obj, String text) {
if((view instanceof ImageView) && (obj instanceof Drawable)) {
ImageView imageView = (ImageView) view;
BitmapDrawable thumbnail = (BitmapDrawable) obj;
imageView.setImageDrawable((Drawable)thumbnail);
return true;
}
return false;
}
}
We will use the upload CGI command to save the title and notes.
upload.cgi
.
UPLOAD=1
in the
CONFIG file
WRITEPROTECT
commandUPDIR
commandFTIME
commandAdd
upload()
, which uses
upload.cgi
by POST request, to the
FlashAirRequest.java file from
Android Tutorial 3: Downloading Content.
static public String upload(String command, String filename, String saveString) {
String result = "";
final String boundary = "========================";
try {
URL url = new URL(command);
HttpURLConnection httpUrlCon = (HttpURLConnection)url.openConnection();
httpUrlCon.setDoInput(true);
httpUrlCon.setDoOutput(true);
httpUrlCon.setUseCaches(false);
httpUrlCon.setRequestMethod("POST");
httpUrlCon.setRequestProperty("Charset", "UTF-8");
httpUrlCon.setRequestProperty("Content-Type",
"multipart/form-data;boundary="+ boundary);
DataOutputStream ds = new DataOutputStream(httpUrlCon.getOutputStream());
ds.writeBytes("--" + boundary + "\r\n");
ds.writeBytes("Content-Disposition: form-data; name=\"upload.cgi\";
filename=\"" + filename +"\""+"\r\n");
ds.writeBytes( "\r\n" );
ds.write(saveString.getBytes("UTF-8"));
ds.writeBytes( "\r\n" );
ds.writeBytes("--" + boundary + "--" + "\r\n");
ds.flush();
ds.close();
if(httpUrlCon.getResponseCode() == HttpURLConnection.HTTP_OK){
StringBuffer sb = new StringBuffer();
InputStream is = httpUrlCon.getInputStream();
byte[] data = new byte[1024];
int leng = -1;
while((leng = is.read(data)) != -1) {
sb.append(new String(data, 0, leng));
}
result = sb.toString();
}
} catch (MalformedURLException e) {
Log.e("ERROR", "ERROR: " + e.toString());
e.printStackTrace();
} catch (IOException e) {
Log.e("ERROR", "ERROR: " + e.toString());
e.printStackTrace();
}
return result;
}
public void saveNewTitleMemo() {
new AsyncTask<String, Void, String>(){
@Override
protected String doInBackground(String... params) {
String parameter = "?WRITEPROTECT=ON&UPDIR=/" + rootDir;
parameter = parameter + "&FTIME=" + getDateTime16();
String filename = date.replaceAll("/", "") + ".txt";
String rtnStr = "";
rtnStr = FlashAirRequest.getString(params[0] + parameter);
if(rtnStr.toUpperCase(Locale.getDefault()).equals("SUCCESS")) {
rtnStr = FlashAirRequest
.upload( params[0], filename, newTitle + "\n" + newMemo);
}
return rtnStr;
}
@Override
protected void onPostExecute(String result) {
if(result.toUpperCase(Locale.getDefault()).indexOf("SUCCESS") >= 0) {
Toast.makeText(MemoEditActivity.this, "Save Completed.",
Toast.LENGTH_LONG).show();
}
}
}.execute("http://flashair/upload.cgi");
}
getDateTime16()
generates a date string from the current date to indicate
the creation
date of the file we're uploading. It creates a date and a time as 16-bit hexadecimal
values,
and concatinates them.
upload()
.
The method generates a date string from the current date to indicate the creation date of the file we're uploading, as previously mentioned.
public String getDateTime16() {
Calendar calendar = Calendar.getInstance();
int year = (calendar.get(Calendar.YEAR) - 1980) << 9;
int month = (calendar.get(Calendar.MONTH) + 1) << 5;
int day = calendar.get(Calendar.DAY_OF_MONTH);
int hours = calendar.get(Calendar.HOUR_OF_DAY) << 11;
int minites = calendar.get(Calendar.MINUTE) << 5;
int seconds = calendar.get(Calendar.SECOND) / 2;
String rtnStr = "0x" + Integer.toHexString(year + month + day)
+ Integer.toHexString(hours + minites + seconds);
return rtnStr;
}
The method generates a date string in the 16-bit format, from the target date format(YYYY/MM/DD).
public String getDate16(String date) {
String rtnStr = "";
try{
int year = (Integer.parseInt(date.substring(0,4)) - 1980) << 9;
int month = (Integer.parseInt(date.substring(5,7))) << 5;
int day = Integer.parseInt(date.substring(8,10));
rtnStr = String.valueOf(year + month + day);
}
catch(Exception e) {
Log.e("ERROR", "ERROR: " + e.toString());
e.printStackTrace();
}
return rtnStr;
}
Let's set a title and save some notes.
Enter the title and note and tap the save button.
When you return to the previous screen, you'll see your changes have been saved!
android_tutorial_07.zip (533KB)
All sample code on this page is licensed under BSD 2-Clause License